//---------------------------------------------------------------------
//  This file is part of the Background Motion solution.
// 
//  Copyright (C) Mindscape (TM).  All rights reserved.
//  http://www.mindscape.co.nz
// 
//  THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//---------------------------------------------------------------------

using System;
using System.Collections;
using System.Reflection;
using System.Web.UI;

namespace Mindscape.Web.UI.WebControls.Binding
{
  public static class WebDataBindingHelpers
  {
    public static Control FindControlRecursive(Control root, string id)
    {
      if (root.ID == id || id == "this" || id== "me")
      {
        return root;
      }

      foreach (Control control in root.Controls)
      {
        Control found = FindControlRecursive(control, id);
        if (found != null)
        {
          return found;
        }
      }

      return null;
    }

    public const BindingFlags MemberAccess =
      BindingFlags.Public | BindingFlags.NonPublic |
        BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase;

    public static object GetProperty(object Object, string Property)
    {
      return Object.GetType().GetProperty(Property, MemberAccess).GetValue(Object, null);
    }

    public static object GetField(object Object, string Property)
    {
      return Object.GetType().GetField(Property, MemberAccess).GetValue(Object);
    }

    public static Type GetPropertyType(object Object, string Property)
    {
      return Object.GetType().GetProperty(Property, MemberAccess).PropertyType;
    }

    private static object GetPropertyInternal(object Parent, string Property)
    {
      if (Property == "this" || Property == "me")
      {
        return Parent;
      }

      object Result;
      string PureProperty = Property;
      string Indexes = null;
      bool IsArrayOrCollection = false;

      if (Property.IndexOf("[") > -1)
      {
        PureProperty = Property.Substring(0, Property.IndexOf("["));
        Indexes = Property.Substring(Property.IndexOf("["));
        IsArrayOrCollection = true;
      }

      MemberInfo Member = Parent.GetType().GetMember(PureProperty, MemberAccess)[0];
      if (Member.MemberType == MemberTypes.Property)
      {
        Result = ((PropertyInfo)Member).GetValue(Parent, null);
      }
      else
      {
        Result = ((FieldInfo)Member).GetValue(Parent);
      }

      if (IsArrayOrCollection)
      {
        Indexes = Indexes.Replace("[", "").Replace("]", "");

        if (Result is Array)
        {
          int Index = -1;
          int.TryParse(Indexes, out Index);
          Result = CallMethod(Result, "GetValue", Index);
        }
        else if (Result is ICollection)
        {
          if (Indexes.StartsWith("\""))
          {
            Indexes = Indexes.Trim('\"');
            Result = CallMethod(Result, "get_Item", Indexes);
          }
          else
          {
            int Index = -1;
            int.TryParse(Indexes, out Index);
            Result = CallMethod(Result, "get_Item", Index);
          }
        }
      }

      return Result;
    }

    private static object SetPropertyInternal(object Parent, string Property, object Value)
    {
      object Result;
      string PureProperty = Property;
      string Indexes = null;
      bool IsArrayOrCollection = false;

      if (Property == "this" || Property == "me")
      {
        return Parent;
      }

      if (Property.IndexOf("[") > -1)
      {
        PureProperty = Property.Substring(0, Property.IndexOf("["));
        Indexes = Property.Substring(Property.IndexOf("["));
        IsArrayOrCollection = true;
      }

      if (!IsArrayOrCollection)
      {
        MemberInfo Member = Parent.GetType().GetMember(PureProperty, MemberAccess)[0];
        if (Member.MemberType == MemberTypes.Property)
        {
          ((PropertyInfo)Member).SetValue(Parent, Value, null);
        }
        else
        {
          ((FieldInfo)Member).SetValue(Parent, Value);
        }
        return null;
      }
      else
      {
        MemberInfo Member = Parent.GetType().GetMember(PureProperty, MemberAccess)[0];
        if (Member.MemberType == MemberTypes.Property)
        {
          Result = ((PropertyInfo)Member).GetValue(Parent, null);
        }
        else
        {
          Result = ((FieldInfo)Member).GetValue(Parent);
        }
      }

      if (IsArrayOrCollection)
      {
        Indexes = Indexes.Replace("[", "").Replace("]", "");

        if (Result is Array)
        {
          int Index = -1;
          int.TryParse(Indexes, out Index);
          Result = CallMethod(Result, "SetValue", Value, Index);
        }
        else if (Result is ICollection)
        {
          if (Indexes.StartsWith("\""))
          {
            Indexes = Indexes.Trim('\"');
            Result = CallMethod(Result, "set_Item", Indexes, Value);
          }
          else
          {
            int Index = -1;
            int.TryParse(Indexes, out Index);
            Result = CallMethod(Result, "set_Item", Index, Value);
          }
        }
      }

      return Result;
    }

    public static object GetPropertyEx(object Parent, string Property)
    {
      int lnAt = Property.IndexOf(".");
      if (lnAt < 0)
      {
        return GetPropertyInternal(Parent, Property);
      }

      string Main = Property.Substring(0, lnAt);
      string Subs = Property.Substring(lnAt + 1);

      object Sub = GetPropertyInternal(Parent, Main);
      return GetPropertyEx(Sub, Subs);
    }

    public static void SetProperty(object Object, string Property, object Value)
    {
      Object.GetType().GetProperty(Property, MemberAccess).SetValue(Object, Value, null);
    }

    public static void SetField(object Object, string Property, object Value)
    {
      Object.GetType().GetField(Property, MemberAccess).SetValue(Object, Value);
    }

    public static object SetPropertyEx(object Parent, string Property, object Value)
    {
      int lnAt = Property.IndexOf(".");
      if (lnAt < 0)
      {
        SetPropertyInternal(Parent, Property, Value);
        return null;
      }

      string Main = Property.Substring(0, lnAt);
      string Subs = Property.Substring(lnAt + 1);

      object Sub = GetPropertyInternal(Parent, Main);

      SetPropertyEx(Sub, Subs, Value);

      return null;
    }

    public static object CallMethod(object Object, string Method, params object[] Params)
    {
      return Object.GetType().InvokeMember(Method, MemberAccess | BindingFlags.InvokeMethod, null, Object, Params);
    }

    public static object CallMethodEx(object Parent, string Method, params object[] Params)
    {
      int lnAt = Method.IndexOf(".");
      if (lnAt < 0)
      {
        return CallMethod(Parent, Method, Params);
      }

      string Main = Method.Substring(0, lnAt);
      string Subs = Method.Substring(lnAt + 1);

      object Sub = GetPropertyInternal(Parent, Main);

      return CallMethodEx(Sub, Subs, Params);
    }
  }
}